local conf = require("conf")
local utils = require("utils")
local language = require("core.language")

local runner = language.get_code_runner()

local helper = {}

-- 添加终端方向状态跟踪，实现螺旋式方向切换
helper.terminal_direction_index = 1  -- 初始方向索引
helper.terminals = {}  -- 存储所有创建的终端实例

local regex_error_jumps = {
    c = "(.+%.c):(%d+):(%d+)",
    go = "(.+%.go):(%d+):(%d+)",
    cpp = "(.+%.cpp):(%d+):(%d+)",
    rust = " *%-*>? ?(.+.%rs):(%d+):(%d+)",
    python = '.*"(.+%.py)".*line (%d+).*',
    lua = "l?u?a?:? ?(.+%.lua):(%d+)",
}

local function terminal_cmd(command)
    local ok, tree_api = pcall(require, "nvim-tree.api")

    if not ok then
        return vim.cmd(command)
    end

    for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
        if vim.api.nvim_buf_get_option(bufnr, "filetype") == "NvimTree" then
            tree_api.tree.toggle({ focus = false })
            vim.cmd(command)
            tree_api.tree.toggle({ focus = false })
            return
        end
    end

    vim.cmd(command)
end

local function create_shell_term(direction, count, cmd)
    -- 使用动态方式查找shell路径，确保便携性
    local shell_path = vim.o.shell -- 默认使用系统shell

    -- 尝试查找相对路径中的sh.exe
    local function find_relative_sh()
        -- 获取当前Neovim配置目录
        local config_dir = vim.fn.stdpath('config')
        local possible_paths = {
            -- 相对当前配置目录的路径
            utils.path.join(config_dir, '..', '..', '..', 'bin', 'sh.exe'),
            utils.path.join(config_dir, '..', '..', 'bin', 'sh.exe'),
            utils.path.join(config_dir, '..', 'bin', 'sh.exe'),
            utils.path.join(config_dir, 'bin', 'sh.exe'),
            -- 在PATH中查找常见的shell
            'sh.exe',
            'bash.exe',
            'cmd.exe'
        }

        for _, path in ipairs(possible_paths) do
            if vim.fn.executable(path) == 1 then
                return path
            end
        end
        return nil
    end

    -- 尝试使用相对路径查找sh.exe
    local relative_sh = find_relative_sh()
    if relative_sh then
        shell_path = relative_sh
    end

    return helper.terminal:new({
    hidden = true,
    count = count,
    direction = direction,
    cmd = cmd or nil,
    shell = shell_path, -- 明确指定shell路径
    on_open = function(term)
    vim.defer_fn(function()
        vim.cmd("startinsert")
    end, 0)

    -- 重新定义ESC键在终端模式下的行为，让它直接传递给子终端而不触发主NVIM的状态变化
    pcall(vim.keymap.set, "t", "<esc>", "<esc>", { noremap = true, buffer = term.bufnr })
            -- 添加<leader><esc>作为退出toggleterm终端模式的快捷键
            utils.map.register({
                mode = { "t" },
                lhs = "<leader><esc>",
                rhs = "<c-\\><c-n>",
                options = { silent = true, buffer = term.bufnr },
                description = "Escape terminal insert mode",
            })

            -- 保留原有的ctrl-\ ctrl-n映射作为备用
            utils.map.register({
                mode = { "t" },
                lhs = "<c-\\><c-n>",
                rhs = "<c-\\><c-n>",
                options = { silent = true, buffer = term.bufnr },
                description = "Escape terminal insert mode",
            })
        end,
    })
end

local function create_float_term()
    return create_shell_term("float", 120)
end

-- 动态查找lazygit.exe的路径
local function find_relative_lazygit()
    -- 获取当前Neovim配置目录
    local config_dir = vim.fn.stdpath('config')
    local possible_paths = {
        -- 相对当前配置目录的路径
        utils.path.join(config_dir, '..', '..', '..', 'bin', 'lazygit.exe'),
        utils.path.join(config_dir, '..', '..', 'bin', 'lazygit.exe'),
        utils.path.join(config_dir, '..', 'bin', 'lazygit.exe'),
        utils.path.join(config_dir, 'bin', 'lazygit.exe'),
        -- 在PATH中查找
        'lazygit.exe',
        'lazygit'
    }

    for _, path in ipairs(possible_paths) do
        if vim.fn.executable(path) == 1 then
            return path
        end
    end
    return "lazygit" -- 默认返回命令名，让系统在PATH中查找
end

local function jump_err_file()
    local first_file_info = {}

    for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
        local filetype = vim.api.nvim_buf_get_option(bufnr, "filetype")

        if vim.tbl_contains(vim.tbl_keys(regex_error_jumps), filetype) then
            local rule = regex_error_jumps[filetype]
            local line = vim.api.nvim_get_current_line()
            local filepath, row, col = line:match(rule)
            local current_file = vim.api.nvim_buf_get_name(bufnr)

            if vim.tbl_isempty(first_file_info) then
                first_file_info.filepath = filepath
                first_file_info.row = row
                first_file_info.col = col
            end

            if
                vim.fn.fnamemodify(filepath, ":t")
                == vim.fn.fnamemodify(current_file, ":t")
            then
                local win = vim.fn.win_findbuf(bufnr)[1]

                if win == nil or not vim.api.nvim_win_is_valid(win) then
                    break
                end

                vim.api.nvim_set_current_win(win)
                vim.api.nvim_win_set_cursor(
                    0,
                    { tonumber(row or 0), tonumber(col or 0) }
                )
                return
            else
                vim.api.nvim_echo(
                    { { "Can't goto err line", "WarningMsg" } },
                    false,
                    {}
                )
            end
        end
    end

    if not vim.tbl_isempty(first_file_info) then
        vim.cmd(":wincmd k")
        vim.cmd("e " .. first_file_info.filepath)
        vim.api.nvim_win_set_cursor(0, {
            tonumber(first_file_info.row or 0),
            tonumber(first_file_info.col or 0),
        })
    end
end

local function code_runner(ctx)
    local config_path = utils.path.join(vim.fn.getcwd(), "workspace.json")

    local execute = ""
    local count = vim.api.nvim_eval("v:count1")
    local command = [[exe %d."TermExec cmd='%s' go_back=0"]]

    if config_path and utils.path.exists(config_path) then
        local content = table.concat(vim.fn.readfile(config_path), "\n")
        execute = vim.json.decode(content)["execute"]
    else
        local callback = runner[vim.opt.filetype:get()]
        if callback then
            execute = callback()
        end
    end

    if execute:len() > 0 then
        if execute:sub(1, 1) == ":" then
            vim.cmd(execute)
        else
            terminal_cmd(command:format(count, execute))
        end
    else
        vim.notify(
            "Not found code runner conf",
            "WARN",
            { annote = "[SimpleNvim]", key = "[SimpleNvim]" }
        )
    end
end

-- 螺旋式方向切换函数
function helper.get_next_direction()
    -- 只在水平同垂直方向之间切换，排除浮动终端
    local directions = {"horizontal", "vertical"}
    local direction = directions[helper.terminal_direction_index]

    -- 确保方向是有效的（额外的安全检查）
    if direction ~= "horizontal" and direction ~= "vertical" then
        direction = "horizontal"  -- 默认为水平方向
    end

    -- 更新索引，实现螺旋式切换
    helper.terminal_direction_index = (helper.terminal_direction_index % #directions) + 1

    return direction
end

-- 创建新终端的函数，确保每次都有独立的缓冲区
function helper.create_new_terminal(direction)
    local Terminal = require('toggleterm.terminal').Terminal
    local term = Terminal:new({
        direction = direction,
        -- 使用唯一计数确保缓冲区独立
        count = math.random(1000, 9999),
        shade_filetypes = {},
        shade_terminals = false,
        shell = vim.o.shell,
        auto_scroll = true,
    })
    return term
end

function helper.load()
    helper.terminal = require("toggleterm.terminal").Terminal

    helper.float_term = create_float_term()

    vim.api.nvim_create_user_command(
        "CodeRunner",
        code_runner,
        { desc = "Code Run in toggleterm" }
    )
end

function helper.on_open(term)
    vim.wo.spell = false
    vim.wo.winfixbuf = true

    -- 保存原始的vim.schedule函数引用
    local original_schedule = vim.schedule

    -- 重写vim.schedule来捕获缓冲区切换相关错误
    -- 但保留原始功能，确保参数传递和返回值处理正确
    if vim.wo.winfixbuf and term.bufnr then
        vim.schedule = function(callback)
            -- 使用pcall包装执行，防止缓冲区切换错误导致的问题
            local wrapped_callback = function()
                local success, err = pcall(callback)
                if not success then
                    -- 检查是否是缓冲区相关的错误
                    local is_buffer_error = false
                    local check_success, check_result = pcall(function()
                        local err_str = tostring(err)
                        return err_str:find("Cannot switch buffer") or
                               err_str:find("'winfixbuf' is enabled") or
                               err_str:find("E153")
                    end)

                    is_buffer_error = check_success and check_result

                    -- 对于缓冲区错误，静默忽略；其他错误尝试记录
                    if not is_buffer_error then
                        local log_success = pcall(function()
                            if vim.notify then
                                vim.notify('Error in scheduled callback: ' .. tostring(err), vim.log.levels.ERROR, { title = "ToggleTerm" })
                            end
                        end)
                    end
                end
            end

            -- 调用原始的schedule函数，并传递包装后的回调
            original_schedule(wrapped_callback)
        end
    end

    -- 确保进入插入模式，让ESC键能正确传递给子终端
    vim.defer_fn(function()
        vim.cmd("startinsert")
    end, 0)

    -- 重新定义ESC键在终端模式下的行为，让它直接传递给子终端而不触发主NVIM的状态变化
    pcall(vim.keymap.set, "t", "<esc>", "<esc>", { noremap = true, buffer = term.bufnr })

    -- 添加<leader><esc>作为退出toggleterm终端模式的快捷键
    utils.map.register({
        mode = { "t" },
        lhs = "<leader><esc>",
        rhs = "<c-\\><c-n>",
        options = { silent = true, buffer = term.bufnr },
        description = "Escape terminal insert mode",
    })

    -- 保留原有的ctrl-\ ctrl-n映射作为备用
    utils.map.register({
        mode = { "t" },
        lhs = "<c-\\><c-n>",
        rhs = "<c-\\><c-n>",
        options = { silent = true, buffer = term.bufnr },
        description = "Escape terminal insert mode",
    })

    -- 添加额外的防护措施，确保在终端模式下，ESC键不会触发主NVIM的状态变化
    vim.api.nvim_buf_set_keymap(term.bufnr, "t", "<esc>", "<esc>", { noremap = true, silent = true })

    -- 添加缓冲区切换快捷键
    utils.map.register({
        mode = { "t" },
        lhs = "<c-h>",
        rhs = "<c-\\><c-n><c-w>h",
        options = { silent = true, buffer = term.bufnr },
        description = "Switch to left window",
    })
    utils.map.register({
        mode = { "t" },
        lhs = "<c-l>",
        rhs = "<c-\\><c-n><c-w>l",
        options = { silent = true, buffer = term.bufnr },
        description = "Switch to right window",
    })

    utils.map.register({
        mode = { "n" },
        lhs = "gf",
        rhs = jump_err_file,
        options = { silent = true, buffer = term.bufnr },
        description = "Quick jump to err file",
    })
end

function helper.switch_float_term()
    -- 确保浮动终端的缓冲区已经初始化
    if not helper.float_term.bufnr or helper.float_term.bufnr == 0 then
        helper.float_term:toggle()
    else
        -- 直接切换浮动终端状态
        helper.float_term:toggle()
    end
end

-- 新的终端切换逻辑：实现螺旋式方向切换同独立缓冲区
function helper.switch_new_terminal()
    -- 获取下一个方向（排除浮动终端）
    local next_direction = helper.get_next_direction()

    -- 创建新的终端实例
    local new_term = helper.create_new_terminal(next_direction)

    -- 将新终端添加到终端列表中
    table.insert(helper.terminals, new_term)

    -- 打开终端
    new_term:open(nil, next_direction)
end

-- 修改底部终端函数为新的终端创建函数
function helper.switch_bottom_term()
    -- 直接使用新的螺旋式终端创建函数
    helper.switch_new_terminal()
end

-- 移除右侧终端函数（或者将其重定向到新的终端创建函数）
function helper.switch_right_term()
    -- 由于我们移除了右侧终端选项，这里直接使用新的终端创建函数
    helper.switch_new_terminal()
end

-- 移除 switch_all_term 函数
function helper.switch_all_term()
    -- 移除此功能，因为无意义
    -- terminal_cmd("ToggleTermToggleAll")
end

return helper

